package com.lagou.rpc;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;

import java.util.List;

public class RpcDecoder extends ByteToMessageDecoder {

    private Class<?> clazz;
    private Serializer serializer;

    public RpcDecoder(Class<?> clazz, Serializer serializer) {
        this.clazz = clazz;
        this.serializer = serializer;
    }

    /**
     * 这里在手动解决粘拆包问题, 可使用 LengthFieldBasedFrameDecoder 和 LengthFieldPrepender
     * 即编码自动添加报文长度, 解码则会根据偏移量长度进行解码, 不足则等待字节到来, 大于则会根据长度拆包
     *
     * 可以自定义协议: 报文 = 分割符字段4字节 + 长度字段4字节 + 请求类型字典1字节 + 预留字段1字节 = 10字节
     *
     * @param channelHandlerContext
     * @param byteBuf
     * @param list
     * @throws Exception
     */
    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
        // 跳过编码时加入报文长度(一个int 4字节), 继续等待字节到来
        if (byteBuf.readableBytes() < 4) {
            return;
        }

        // 报文长度(编码时写入的int)
        int len = byteBuf.readInt();

        // 标记字节流读开始位置
        byteBuf.markReaderIndex();

        //
        if (byteBuf.readableBytes() < len) {
            // 重置读索引为之前标记的 markReaderIndex
            byteBuf.resetReaderIndex();
            return;
        }
        byte[] data = new byte[len];
        byteBuf.readBytes(data);
        Object obj = serializer.deserialize(clazz, data);
        list.add(obj);

        // 回收已读字节
        byteBuf.discardReadBytes();
    }
}
